﻿using System;
using Microsoft.Practices.Unity.InterceptionExtension;
using Sapit.Base;
using Sapit.Helpers;
using Sapit.Unity.Attributes;

namespace Sapit.Unity
{
    public class CircuitBreakerHandler : CircuitBreakerBase, ICallHandler
    {
        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
        {
            var circuitBreaker = AttributeHelper.GetAttributeIfPresent<CircuitBreakerAttribute>(input.MethodBase);
            if (circuitBreaker != null)
            {
                Invocation inv = new Invocation() { InvocationTarget = input.Target, Method = input.MethodBase };

                if (ThisInvocationIsShutOff(inv, circuitBreaker))
                {
                    if (ThisInvocationShouldBeTurnedBackOn(inv, circuitBreaker))
                    {
                        TurnThisInvocationBackOn(inv);
                        return TryProceed(input, inv, getNext);
                    }
                    else
                    {
                        return PerformShutOffBehavior(input, circuitBreaker);
                    }
                }
                else
                {
                    return TryProceed(input, inv, getNext);
                }
            }
            else
            {
                return getNext()(input, getNext);
            }
        }

        private static IMethodReturn TryProceed(IMethodInvocation invocation, Invocation inv, GetNextHandlerDelegate getNext)
        {
            IMethodReturn msg = getNext()(invocation, getNext);
            if (msg.Exception != null)
            {
                RegisterFailure(inv);
            }
            return msg;
        }

        private static IMethodReturn PerformShutOffBehavior(IMethodInvocation invocation, CircuitBreakerAttribute circuitBreaker)
        {
            switch (circuitBreaker.BehaviorWhenShutOff)
            {
                case Behavior.Undefined:
                case Behavior.ReturnDefaultValue:
                    return invocation.CreateMethodReturn(circuitBreaker.ShutOffReturnValue, null);
                case Behavior.ThrowException:
                    return invocation.CreateExceptionMethodReturn(new InvalidOperationException(string.Format("{0}", circuitBreaker.ShutOffReturnValue)));
                default:
                    return invocation.CreateMethodReturn(circuitBreaker.ShutOffReturnValue, null);
            }
        }

        public int Order { get; set; }
    }
}
